home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2005 March / Macworld CD March 2005 - Marathon Trilogy.iso / Shareware World / Text Processing / HexEdit Release.sit / HexEdit Release / Project / Source / HexSearch.c < prev    next >
Encoding:
C/C++ Source or Header  |  2004-10-31  |  15.2 KB  |  584 lines  |  [TEXT/CWIE]

  1. /*
  2.  * The contents of this file are subject to the Mozilla Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/MPL/
  6.  * 
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  * 
  12.  * The Original Code is Copyright 1993 Jim Bumgardner.
  13.  * 
  14.  * The Initial Developer of the Original Code is Jim Bumgardner
  15.  * Portions created by Lane Roathe are
  16.  * Copyright (C) Copyright © 1996-2002.
  17.  * All Rights Reserved.
  18.  *
  19.  * Modified: $Date: 2004/10/31 19:09:58 $
  20.  * Revision: $Id: HexSearch.c,v 1.28 2004/10/31 19:09:58 raving Exp $
  21.  *
  22.  * Contributor(s):
  23.  *        Lane Roathe
  24.  *        Nick Shanks
  25.  */
  26.  
  27. // 05/10/01 - GAB: MPW environment support
  28. #ifdef __MPW__
  29. #include "MPWIncludes.h"
  30. #endif
  31.  
  32. #include <stdio.h>
  33. #include <ctype.h>
  34.  
  35. #include "HexSearch.h"
  36. #include "EditRoutines.h"
  37. #include "EditScrollbar.h"
  38. #include "Menus.h"
  39. #include "Prefs.h"
  40. #include "Utility.h"
  41.  
  42. /*** SET SEARCH BUTTONS ***/
  43. void SetSearchButtons( void )
  44. {
  45. // Prevent flashing, adapted from Max Horn's mod
  46.     if( g.searchDlg )
  47.     {
  48.         if( *g.searchText && g.searchDisabled )
  49.         {
  50.             EnableButton( g.searchDlg, SearchForwardItem );
  51.             EnableButton( g.searchDlg, SearchBackwardItem );
  52.             g.searchDisabled = false;
  53.         }
  54.         else if( !*g.searchText && !g.searchDisabled )
  55.         {
  56.             DisableButton( g.searchDlg, SearchForwardItem );
  57.             DisableButton( g.searchDlg, SearchBackwardItem );
  58.             g.searchDisabled = true;
  59.         }
  60.  
  61.         //LR 175 -- set the replace buttons
  62.         //LR 180 -- don't dim replace w/no text as that's a valid replacement!
  63.         if( !g.searchDisabled /*&& *g.replaceText*/ && g.replaceDisabled )
  64.         {
  65.             EnableButton( g.searchDlg, ReplaceItem );
  66.             EnableButton( g.searchDlg, ReplaceAllItem );
  67.             g.replaceDisabled = false;
  68.         }
  69.         else if( (g.searchDisabled /*|| !*g.replaceText*/) && !g.replaceDisabled )
  70.         {
  71.             DisableButton( g.searchDlg, ReplaceItem );
  72.             DisableButton( g.searchDlg, ReplaceAllItem );
  73.             g.replaceDisabled = true;
  74.         }
  75.     }
  76. }
  77.  
  78. // ----------------------------
  79. void OpenSearchDialog( void )
  80. {
  81.     MySetCursor( C_Arrow );
  82.  
  83.     if( !FindFirstEditWindow() )
  84.         return;
  85.  
  86.     // If Dialog Window isn't open
  87.     if( !g.searchDlg )
  88.         g.searchDlg = GetNewDialog( dlgSearch, NULL, kFirstWindowOfClass );
  89.  
  90.     if( g.searchDlg )
  91.     {
  92.         // Convert Existing Search Scrap, if it exists to text
  93.         SetText( g.searchDlg, SearchTextItem, g.searchText );
  94.         SetText( g.searchDlg, ReplaceTextItem, g.replaceText );
  95.  
  96.         // Set Radio Buttons
  97.         SetControl( g.searchDlg, HexModeItem, gPrefs.searchMode == EM_Hex );
  98.         SetControl( g.searchDlg, AsciiModeItem, gPrefs.searchMode == EM_Ascii );
  99.         SetControl( g.searchDlg, MatchCaseItem, gPrefs.searchCase );
  100.         SetControl( g.searchDlg, WrapItem, gPrefs.wrapFinds );
  101.         if( EM_Hex == gPrefs.searchMode )
  102.             DisableButton( g.searchDlg, MatchCaseItem );    // LR 1.65
  103.         else
  104.             EnableButton( g.searchDlg, MatchCaseItem );    // LR 1.65
  105.  
  106.         // NS: added disabling of buttons if no text is entered
  107. // LR: not needed            GetText( g.searchDlg, SearchTextItem, g.searchText );
  108.         SelectDialogItemText( g.searchDlg, SearchTextItem, 0, 32767 );    // LR: make immediately editable
  109.         g.searchDisabled = false;
  110.         SetSearchButtons();
  111.     }
  112.  
  113.     SelectWindow( GetDialogWindow( g.searchDlg ) );
  114.     ShowWindow( GetDialogWindow( g.searchDlg ) );
  115. }
  116.  
  117. /*** PERFORM TEXT SEARCH ***/
  118. Boolean PerformTextSearch( EditWindowPtr dWin, SearchUIFlag uiSkipFlag )    //LR 175 -- now return if search succeeds (for replace)
  119. {
  120.     short        ch, matchIdx;
  121.     long        addr, matchAddr = 0, adjust;
  122.     register     EditChunk **cc;
  123.     long         wrapped =false;
  124.  
  125.     // LR: v1.6.5 if not passed a window, get first one
  126.     if( !dWin )
  127.     {
  128.         dWin = FindFirstEditWindow();
  129.         if( !dWin )
  130.             return( false );
  131.     }
  132.  
  133.     // Get starting index into file
  134.     addr = dWin->startSel;
  135.     if( gPrefs.searchForward )
  136.         adjust = 1;
  137.     else
  138.         adjust = -1;
  139.  
  140.     if( !uiSkipFlag && dWin->startSel != dWin->endSel )    //LR 191 -- adjust only if there is a selection
  141.     {
  142.         MySetCursor( C_Watch );
  143.         addr += adjust;            //LR 189 -- bug fix, find needs this, just not replace all!
  144.     }
  145.  
  146. wrap:
  147.     matchIdx = 0;
  148.  
  149.     //LR 185 -- we handle the chucks ourself to speed up searching!
  150.     //            get the chunk for the current address & load it.
  151.  
  152.     cc = GetChunkByAddr( dWin, addr );
  153.     if( !cc )
  154.         goto Failure;    // should never happen, but...
  155.  
  156.     if( !(*cc)->loaded ) LoadChunk( dWin, cc );
  157.  
  158.     // LR: 1.72 -- make sure we are searching in OK memory (ie, empty window bug fix)
  159.     //LR 190 -- allow for wrapping
  160.     while( addr >= 0 && addr < dWin->fileSize )
  161.     {
  162.         if( !(addr & 0xFFFF) )        //LR 1.72 -- don't slow our searches down unnecessarily!
  163.         {
  164.             if( CheckForAbort() )    //LR: 1.66 - allow user to abort the search
  165.                 break;
  166.         }
  167.  
  168.         //LR 190 -- check for wrapping (We do this to avoid infinity if nothing found or too keep finding a single occurance)
  169.         if( gPrefs.wrapFinds && wrapped )
  170.         {
  171.             if( gPrefs.searchForward )
  172.             {
  173.                 if( addr >= dWin->startSel )    // wrap occurs if we get past 
  174.                     break;
  175.             }
  176.             else
  177.             {
  178.                 if( addr <= dWin->startSel )    // where we started after wrapping -- exit search in that case!
  179.                     break;
  180.             }
  181.         }
  182.  
  183. //LR 185 -- replace with much faster inline code (10x at least!)        ch = GetByte( dWin, addr );
  184.         ch = (Byte) (*(*cc)->data)[addr - (*cc)->addr];
  185.         if( !gPrefs.searchCase && gPrefs.searchMode != EM_Hex )
  186.             ch = toupper( ch );
  187.         if( ch == g.searchBuffer[matchIdx+1] )
  188.         {
  189.             if( matchIdx == 0 )
  190.                 matchAddr = addr;
  191.             ++matchIdx;
  192.             if( matchIdx >= g.searchBuffer[0] )
  193.                 goto Success;
  194.             ++addr;
  195.             if( addr == dWin->fileSize )
  196.             {
  197.                 matchIdx = 0;
  198.                 addr = matchAddr;
  199.             }
  200.             else
  201.                 continue;
  202.         }
  203.         else if( matchIdx )    // if we were in a match, back it out!
  204.         {
  205.             matchIdx = 0;
  206.             addr = matchAddr;
  207.         }
  208.         addr += adjust;
  209.  
  210.         //LR 185 -- OK, here we must handle moving to a new chunk if outside current one
  211.         if( addr < (*cc)->addr )
  212.         {
  213.             UnloadChunk( dWin, cc, true );
  214.             cc = (*cc)->prev;
  215.             goto newchunk;
  216.         }
  217.         else if( addr >= (*cc)->addr + (*cc)->size )
  218.         {
  219.             UnloadChunk( dWin, cc, true );
  220.             cc = (*cc)->next;
  221. newchunk:
  222.             if( !cc )
  223.                 break;;
  224.  
  225.             LoadChunk( dWin, cc );    // no check, most likely not loaded, and checked in routine anyway
  226.         }
  227.     }
  228.  
  229.     //LR 190 -- check if we want to wrap around
  230.     if( gPrefs.wrapFinds && !wrapped )
  231.     {
  232.         if( gPrefs.searchForward )
  233.             addr = 0;
  234.         else
  235.             addr = dWin->fileSize - 1;
  236.  
  237.         wrapped = true;    // we have wrapped, set flag
  238.         goto wrap;
  239.     }
  240.  
  241. Failure:
  242.     SysBeep( 1 );
  243.     MySetCursor( C_Arrow );
  244.     return( false );
  245.  
  246. Success:
  247.     dWin->startSel = matchAddr;
  248.     dWin->endSel = dWin->startSel + g.searchBuffer[0];
  249.  
  250.     //LR 188 -- only update UI if desired (ie, speed up replace all)
  251.     if( !uiSkipFlag )
  252.     {
  253. /* LR 188 -- why bring window to foreground?
  254.         if( dWin != (EditWindowPtr) GetWRefCon( FrontNonFloatingWindow() ) )
  255.             SelectWindow( dWin->oWin.theWin );
  256. */
  257.         ScrollToSelection( dWin, dWin->startSel, true );
  258.         MySetCursor( C_Arrow );
  259.     }
  260.     return( true );
  261. }
  262.  
  263. #define GGotoAddr        1
  264. #define GAddrItem        3
  265. #define GHexItem        4
  266. #define GDecimalItem    5
  267. // #define GUserItem        6
  268.  
  269. /*** GOTO USER ITEM ***/
  270. /* 1.65 LR -- removed ... what was this?
  271. static pascal void GotoUserItem( DialogPtr dp, short itemNbr )
  272. {
  273.     switch( itemNbr )
  274.     {
  275.         case GUserItem:
  276. //             MyOutlineButton( dp, 1, qd.black );
  277.             SetDialogDefaultItem( dp, itemNbr );        // NS: above function removed
  278.             break;
  279.     }
  280. }
  281. */
  282.  
  283. /*** OPEN GOTO ADDRESS ***/
  284. OSStatus OpenGotoAddress( void )
  285. {
  286. //     GrafPtr        savePort;
  287. //     short        itemHit;
  288. //     short        t;
  289. //     Handle        h;
  290. //     Rect        r;
  291.  
  292. //     GetPort( &savePort );
  293.     MySetCursor( C_Arrow );
  294.  
  295.     if( !FindFirstEditWindow() )
  296.         return paramErr;
  297.  
  298.     if( !g.gotoDlg )
  299.         g.gotoDlg = GetNewDialog( dlgGoto, NULL, kFirstWindowOfClass );
  300.     if( !g.gotoDlg )
  301.         return paramErr;
  302.  
  303.     SetText( g.gotoDlg, GAddrItem, g.gotoText );
  304.     // Set Radio Buttons
  305.     SetControl( g.gotoDlg, GHexItem, gPrefs.gotoMode == EM_Hex );
  306.     SetControl( g.gotoDlg, GDecimalItem, gPrefs.gotoMode == EM_Decimal );
  307.     SelectDialogItemText( g.gotoDlg, GAddrItem, 0, 32767 );
  308.  
  309.     SelectWindow( GetDialogWindow( g.gotoDlg ) );
  310.     ShowWindow( GetDialogWindow( g.gotoDlg ) );
  311.  
  312.     return noErr;
  313. }
  314.  
  315. /*** DO MODELESS DIALOG EVENT ***/
  316. void DoModelessDialogEvent( EventRecord *theEvent )
  317. {
  318.     DialogPtr    whichDlog;
  319.     short        itemHit;
  320.  
  321. //#if TARGET_API_MAC_CARBON    // LR: v1.6.5 do this for all refs
  322. //LR: 1.7 -- fix for OS X, which requires below code    whichDlog = (DialogRef)FrontWindow();
  323.     whichDlog = GetDialogFromWindow( FrontNonFloatingWindow() );
  324. //#else
  325. //    whichDlog = FrontWindow();
  326. //#endif
  327.  
  328.     // Do Event Filtering
  329.     if( whichDlog && theEvent->what == keyDown )    //LR: 1.66 avoid NULL reference
  330.     {
  331.         // Process Edit Keys
  332.         if( ((theEvent->message & charCodeMask) == kReturnCharCode) ||
  333.             ((theEvent->message & charCodeMask) == kEnterCharCode) )
  334.         {
  335.             itemHit = 1;
  336.             SimulateButtonPress( whichDlog, 1 );
  337.             goto ButtonHit;
  338.         }
  339.         if( theEvent->modifiers & cmdKey )
  340.         {
  341. /*LR - allow all menu options active
  342.             switch ( ( theEvent->message & keyCodeMask ) >> 8 ) {
  343.             case 0x0D:    // W
  344.                 DisposeDialog( g.searchDlg );
  345.                 g.searchDlg = NULL;
  346.                 return;
  347.             case 0x0C:    // Q
  348.                 if( CloseAllEditWindows() )
  349.                     g.quitFlag = true;
  350.                 return;
  351.             }
  352. */
  353.             AdjustMenus();
  354.             HandleMenu( MenuKey( (char) (theEvent->message & charCodeMask) ), theEvent->modifiers );
  355.             return;
  356.         }
  357.     }
  358.  
  359.     // NS: added disabling of buttons if no text is entered
  360.     if( theEvent->what == nullEvent && whichDlog == g.searchDlg )    // LR: v1.6.5 must check which dialog!
  361.     {
  362.         GetText( g.searchDlg, SearchTextItem, g.searchText );
  363.         GetText( g.searchDlg, ReplaceTextItem, g.replaceText );
  364.         SetSearchButtons();
  365.     }
  366.  
  367.     if( DialogSelect( theEvent, &whichDlog, &itemHit ) )
  368.     {
  369.         EditWindowPtr dWin = FindFirstEditWindow();
  370.  
  371. ButtonHit:
  372.         if( whichDlog == g.searchDlg )
  373.         {
  374.             switch( itemHit )
  375.             {
  376.             case SearchForwardItem:
  377.             case SearchBackwardItem:
  378.                 gPrefs.searchForward = ( itemHit == SearchForwardItem );
  379.                 GetText( g.searchDlg, SearchTextItem, g.searchText );
  380.                 if( StringToSearchBuffer( gPrefs.searchCase ) )
  381.                     PerformTextSearch( NULL, kSearchUpdateUI );
  382.                 break;
  383.  
  384.             //LR 175 -- handle new replace options
  385.             case ReplaceItem:
  386.             case ReplaceAllItem:
  387.                 if( dWin )
  388.                 {
  389.                     g.replaceAll = ( itemHit == ReplaceAllItem );
  390.                     GetText( g.searchDlg, ReplaceTextItem, g.replaceText );
  391.                     GetText( g.searchDlg, SearchTextItem, g.searchText );        //LR 190 -- replacement text no longer effected by case flag!
  392.                     if( StringToSearchBuffer( gPrefs.searchCase ) )
  393.                     {
  394.                         EditChunk    **replaceChunk;    //LR 190 -- why static, only used herein!
  395.  
  396.                         replaceChunk = NewChunk( g.replaceText[0], 0, 0, CT_Unwritten );
  397.                         if( replaceChunk )
  398.                         {
  399.                             BlockMoveData( g.replaceText+1, *(*replaceChunk)->data, g.replaceText[0] );
  400.  
  401.                             if( !g.replaceAll )    //LR 190 -- replace is a copy in place, don't search first!!!
  402.                             {
  403.                                 long ss = dWin->startSel;    //LR 188 -- hilight what was replaced!
  404.  
  405.                                 //LR 190 -- only replace if a selection is there (ie, a previous find)
  406.                                 if( dWin->startSel != dWin->endSel )
  407.                                 {
  408.                                     RememberOperation( dWin, EO_Paste, &gUndo );
  409.                                     PasteOperation( dWin, replaceChunk );
  410.                                 }
  411.                                 //LR 190 -- Option key on Replace is "replace and find next"
  412.                                 if( theEvent->modifiers & optionKey )
  413.                                 {
  414.                                     PerformTextSearch( dWin, kSearchUpdateUI );
  415.                                 }
  416.                                 else    // otherwise it's just normal replace
  417.                                 {
  418.                                     dWin->startSel = ss;
  419.                                     dWin->endSel = ss + (*replaceChunk)->size;
  420.                                 }
  421.                             }
  422.                             else while( PerformTextSearch( dWin, kSearchSkipUI ) )
  423.                             {
  424.                                 PasteOperation( dWin, replaceChunk );    // replace all is NOT undoable!
  425.                             }
  426.                             ScrollToSelection( dWin, dWin->startSel, true );
  427.                         }
  428.                         DisposeChunk( NULL, replaceChunk );
  429.                     }
  430.                 }
  431.                 break;
  432.  
  433.             case HexModeItem:
  434.                 gPrefs.searchMode = EM_Hex;
  435.                 DisableButton( g.searchDlg, MatchCaseItem );    // LR 1.65
  436.                 goto setmode;
  437.  
  438.             case AsciiModeItem:
  439.                 gPrefs.searchMode = EM_Ascii;
  440.                 EnableButton( g.searchDlg, MatchCaseItem );    // LR 1.65
  441. setmode:
  442.                 SetControl( g.searchDlg, HexModeItem, gPrefs.searchMode == EM_Hex );
  443.                 SetControl( g.searchDlg, AsciiModeItem, gPrefs.searchMode == EM_Ascii );
  444.                 break;
  445.             case MatchCaseItem:
  446.                 gPrefs.searchCase ^= 1;
  447.                 SetControl( g.searchDlg, MatchCaseItem, gPrefs.searchCase );
  448.                 break;
  449.             case WrapItem:    //LR 190 -- allow finds to wrap
  450.                 gPrefs.wrapFinds ^= 1;
  451.                 SetControl( g.searchDlg, WrapItem, gPrefs.wrapFinds );
  452.                 break;
  453.             case SearchTextItem:    //LR 190 -- nothing to do on text boxes
  454.             case ReplaceTextItem:
  455.                 break;
  456.             }
  457.         }
  458.         else if( whichDlog == g.gotoDlg )    // LR: v1.6.5 moved here to allow modeless dialog
  459.         {
  460.             switch( itemHit )
  461.             {
  462.             case GGotoAddr:
  463.                 {
  464.                     long        addr = -1;
  465.                     short        r;
  466.  
  467.                     if( dWin )
  468.                     {
  469.                         GetText( g.gotoDlg, GAddrItem, g.gotoText );
  470.  
  471.                         CopyPascalStringToC( g.gotoText, (char *)g.gotoText );
  472.  
  473.                         if( gPrefs.gotoMode == EM_Hex )
  474.                             r = sscanf( (char *) g.gotoText, "%lx", &addr );
  475.                         else
  476.                             r = sscanf( (char *) g.gotoText, "%ld", &addr );
  477.  
  478.                         CopyCStringToPascal( (char *) g.gotoText, g.gotoText );
  479.  
  480.                         if( !r )    // LR: v1.6.5 show why values don't work
  481.                             ErrorAlert( ES_Caution, errHexValues );
  482.                         else if( addr >= 0 && addr < dWin->fileSize )
  483.                         {
  484.                             dWin->startSel = dWin->endSel = addr;
  485.                             SelectWindow( dWin->oWin.theWin );
  486.                             ScrollToSelection( dWin, addr, true );
  487.                         }
  488.                         else
  489.                             SysBeep(0);    //LR 180 -- signal error
  490.                     }
  491.                 }
  492.                 break;
  493.                 
  494.             case GHexItem:
  495.                 gPrefs.gotoMode = EM_Hex;
  496.                 goto setgmode;
  497.                 break;
  498.                 
  499.             case GDecimalItem:
  500.                 gPrefs.gotoMode = EM_Decimal;
  501. setgmode:
  502.                 SetControl( g.gotoDlg, GHexItem, gPrefs.gotoMode == EM_Hex );
  503.                 SetControl( g.gotoDlg, GDecimalItem, gPrefs.gotoMode == EM_Decimal );
  504.                 break;
  505.             }
  506.         }
  507.     }
  508.     else if( theEvent->what == updateEvt )    // LR: v1.6.5
  509.     {
  510.         if( whichDlog == g.searchDlg )
  511.             SetDialogDefaultItem( g.searchDlg, SearchForwardItem );
  512.         else if( whichDlog == g.gotoDlg )
  513.             SetDialogDefaultItem( g.gotoDlg, GGotoAddr );
  514.     }
  515. }
  516.  
  517. /*** STRING TO SEARCH BUFFER ***/
  518. Boolean StringToSearchBuffer( Boolean matchCase )
  519. {
  520.     Ptr        sp, dp;
  521.     short    i;
  522.     short    val;
  523.     Boolean    loFlag;
  524.  
  525.     //LR 1.73 :empty buffer is OK, just set dest and exit!
  526.     if( !g.searchText[0] )
  527.     {
  528.         g.searchBuffer[0] = 0;
  529.         return( true );
  530.     }
  531.  
  532.     // Convert String to g.searchBuffer
  533.     if( gPrefs.searchMode == EM_Hex )
  534.     {
  535.         sp = (Ptr) &g.searchText[1];
  536.         dp = (Ptr) &g.searchBuffer[1];
  537.         loFlag = false;
  538.         for( i = 0; i < g.searchText[0]; ++i, ++sp )
  539.         {
  540.             if( *sp == '0' && *( sp+1 ) == 'x' )
  541.             {
  542.                 loFlag = 0;
  543.                 ++sp;
  544.                 ++i;
  545.                 continue;
  546.             }
  547.             if( isspace( *sp ) || ispunct( *sp ) )
  548.             {
  549.                 loFlag = 0;
  550.                 continue;
  551.             }
  552.             if( *sp >= '0' && *sp <= '9' )        val = *sp - '0';
  553.             else if( *sp >= 'A' && *sp <= 'F' )    val = 0x0A + ( *sp - 'A' );
  554.             else if( *sp >= 'a' && *sp <= 'f' )    val = 0x0A + ( *sp - 'a' );
  555.             else goto HexError;
  556.             if( loFlag )
  557.             {
  558.                 *( dp-1 ) = ( *( dp-1 ) << 4 ) | val;
  559.                 loFlag = 0;
  560.             }            
  561.             else
  562.             {
  563.                 *dp = val;
  564.                 ++dp;
  565.                 loFlag = 1;
  566.             }
  567.         }
  568.         g.searchBuffer[0] = (long) dp - (long) &g.searchBuffer[1];
  569.         if( g.searchBuffer[0] == 0 )
  570.             goto HexError;
  571.     }
  572.     else
  573.     {
  574.         BlockMoveData( g.searchText, g.searchBuffer, g.searchText[0]+1 );
  575.         if( !matchCase )
  576.             UppercaseText( (Ptr) g.searchBuffer, g.searchText[0] + 1, smSystemScript );    // NS: function name changed and script constant added
  577.     }
  578.     return true;
  579.     
  580. HexError:
  581.     ErrorAlert( ES_Caution, errHexValues );
  582.     return false;
  583. }
  584.